package com.rickpan.service;

import com.rickpan.entity.PasswordResetCode;
import com.rickpan.entity.User;
import com.rickpan.exception.BusinessException;
import com.rickpan.repository.PasswordResetCodeRepository;
import com.rickpan.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.Optional;

/**
 * 密码重置服务
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class PasswordResetService {

    private final PasswordResetCodeRepository passwordResetCodeRepository;
    private final UserRepository userRepository;
    private final EmailService emailService;
    private final PasswordEncoder passwordEncoder;

    private static final int CODE_LENGTH = 6;
    private static final int CODE_EXPIRY_MINUTES = 10;
    private static final int MAX_CODES_PER_HOUR = 5;
    private static final SecureRandom random = new SecureRandom();

    /**
     * 发送密码重置验证码
     */
    @Transactional
    public void sendResetCode(String email) {
        // 1. 验证邮箱是否存在
        Optional<User> userOpt = userRepository.findByEmail(email);
        if (userOpt.isEmpty()) {
            throw new BusinessException("该邮箱未注册");
        }

        User user = userOpt.get();
        if (!"ACTIVE".equals(user.getStatus().name())) {
            throw new BusinessException("账户已被禁用，无法重置密码");
        }

        // 2. 检查发送频率限制
        LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
        long recentCodesCount = passwordResetCodeRepository.countByEmailAndCreatedAtAfter(email, oneHourAgo);
        if (recentCodesCount >= MAX_CODES_PER_HOUR) {
            throw new BusinessException("发送验证码过于频繁，请1小时后再试");
        }

        // 3. 生成验证码
        String code = generateVerificationCode();
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime expiresAt = now.plusMinutes(CODE_EXPIRY_MINUTES);

        // 4. 保存验证码
        PasswordResetCode resetCode = PasswordResetCode.builder()
                .email(email)
                .code(code)
                .createdAt(now)
                .expiresAt(expiresAt)
                .used(false)
                .build();

        passwordResetCodeRepository.save(resetCode);

        // 5. 发送邮件
        try {
            emailService.sendPasswordResetCode(email, code);
            log.info("密码重置验证码发送成功: email={}, code={}", email, code);
        } catch (Exception e) {
            log.error("发送密码重置验证码邮件失败: email={}", email, e);
            throw new BusinessException("邮件发送失败，请稍后重试");
        }
    }

    /**
     * 验证重置验证码
     */
    @Transactional
    public void verifyResetCode(String email, String code) {
        // 1. 查找有效的验证码
        Optional<PasswordResetCode> resetCodeOpt = passwordResetCodeRepository
                .findValidCodeByEmailAndCode(email, code, LocalDateTime.now());

        if (resetCodeOpt.isEmpty()) {
            throw new BusinessException("验证码无效或已过期");
        }

        PasswordResetCode resetCode = resetCodeOpt.get();

        // 2. 验证码验证成功，但不标记为已使用（等到密码重置时再标记）
        log.info("密码重置验证码验证成功: email={}, code={}", email, code);
    }

    /**
     * 重置密码
     */
    @Transactional
    public void resetPassword(String email, String code, String newPassword) {
        // 1. 再次验证验证码
        Optional<PasswordResetCode> resetCodeOpt = passwordResetCodeRepository
                .findValidCodeByEmailAndCode(email, code, LocalDateTime.now());

        if (resetCodeOpt.isEmpty()) {
            throw new BusinessException("验证码无效或已过期");
        }

        // 2. 查找用户
        Optional<User> userOpt = userRepository.findByEmail(email);
        if (userOpt.isEmpty()) {
            throw new BusinessException("用户不存在");
        }

        User user = userOpt.get();
        PasswordResetCode resetCode = resetCodeOpt.get();

        // 3. 更新密码
        String encodedPassword = passwordEncoder.encode(newPassword);
        user.setPassword(encodedPassword);
        user.setUpdatedAt(LocalDateTime.now());
        userRepository.save(user);

        // 4. 标记验证码为已使用
        resetCode.markAsUsed();
        passwordResetCodeRepository.save(resetCode);

        // 5. 标记该邮箱的所有其他未使用验证码为已使用
        passwordResetCodeRepository.markAllUnusedCodesAsUsed(email, LocalDateTime.now());

        log.info("密码重置成功: email={}, userId={}", email, user.getId());
    }

    /**
     * 生成6位数字验证码
     */
    private String generateVerificationCode() {
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }

    /**
     * 清理过期的验证码（定时任务调用）
     */
    @Transactional
    public void cleanupExpiredCodes() {
        try {
            passwordResetCodeRepository.deleteExpiredCodes(LocalDateTime.now());
            log.info("清理过期验证码完成");
        } catch (Exception e) {
            log.error("清理过期验证码失败", e);
        }
    }
}
